# -*- coding: utf-8 -*-
import re
import pandas as pd

from get_cwe_root import get_cwe_root

from JULIET import JULIET
from PERFORMANCE import PERFORMANCE

class CODEQL:
    '''对 codeql 的 result.csv 进行分析'''
    def __init__(self) -> None:
        # codeql 结果的 csv 文件路径
        self.codeql_result_csv_path='../scan_data/codeql/codeql-scan-result.csv'
        # 存储了 codeql 解析结果 name 字段与 cweid 对应关系的 excel 文件路径
        self.codeql_cwe_name_excel_path='./resource/codeql测试结果与juliet的CWE编号对应关系.xlsx'
        # 存储了 codeql 解析结果 name 字段与 cweid 对应关系的 excel 文件的 sheet 表名字
        self.codeql_cwe_name_excel_sheetname='cwe'
        # 根据 codeql_result_csv_path 生成的，用于存储 codeql 的结果
        self.result=[]
        # name 与 cweid 对应关系字典
        self.name_cweid_dict={}
        # 用于获取 cwe 编号之间继承关系的对象
        self.cwe_root=get_cwe_root()
        # juliet 对象,为 juliet 测试集和一些函数
        self.juliet=JULIET()
        self.get_name_cweid_dict()
        self.get_result_from_csv_file()
        self.analyze_result()
        # # 性能分析
        # self.time_log_path='/home/pudding/work/temp/result/sast_result/sast_result/codeql/3/codeql_time.log'
        # self.create_time_log_path='codeql_create_time.log'
        # self.analyze_time_log_path='codeql_analyze_time.log'
        # self.performance_save_path='codeql_performance.xlsx'
        # # 性能分析-create
        # self.create_top_log_path='/home/pudding/work/temp/result/sast_result/sast_result/codeql/3/codeql_create_database_top.log'
        # self.create_iotop_log_path='/home/pudding/work/temp/result/sast_result/sast_result/codeql/3/codeql_create_database_iotop.log'
        # self.create_top_command='java'
        # self.create_iotop_command='java'
        # self.create_performance_save_path='create_codeql_performance.xlsx'
        # # 性能分析-analyze
        # self.analyze_top_log_path='/home/pudding/work/temp/result/sast_result/sast_result/codeql/3/codeql_analyze_database_top.log'
        # self.analyze_iotop_log_path='/home/pudding/work/temp/result/sast_result/sast_result/codeql/3/codeql_analyze_database_iotop.log'
        # self.analyze_top_command='java'
        # self.analyze_iotop_command='java'
        # self.analyze_performance_save_path='analyze_codeql_performance.xlsx'
        # self.adaptation_performance()
        # # 进行性能分析-create
        # create_performance=PERFORMANCE(self.create_time_log_path,self.create_top_log_path,self.create_iotop_log_path,self.create_top_command,self.create_iotop_command,self.create_performance_save_path)
        # # 进行性能分析-analyze
        # analyze_performance=PERFORMANCE(self.analyze_time_log_path,self.analyze_top_log_path,self.analyze_iotop_log_path,self.analyze_top_command,self.analyze_iotop_command,self.analyze_performance_save_path)
        # # 对 codeql 的 create 阶段和 analyze 阶段的数据进行汇总
        # self.summary_data()
        # # 善后工作,把中间过程文件删掉
        # self.remove_file()

    def adaptation_performance(self):
        '''由于 codeql 有 create 和 analyze 两个步骤,对 performance 类不兼容,要进行一些适配工作'''
        with open(self.time_log_path,'r') as f:
            lines=f.readlines()
        with open(self.create_time_log_path,'w') as f:
            f.writelines(lines[0:2])
        with open(self.analyze_time_log_path,'w') as f:
            f.writelines(lines[2:4])

    def remove_file(self):
        '''善后工作,主要是删文件'''
        os.remove(self.create_time_log_path)
        os.remove(self.analyze_time_log_path)

    def summary_data(self):
        '''对 analyze 和 create 两个阶段的性能分析数据进行汇总'''
        def translate_rate_to_float(rate):
            '''把360.4KB/s转换成360400'''
            if 'GB/s' in rate:
                return float(rate[:-4])*1000*1000*1000
            if 'MB/s' in rate:
                return float(rate[:-4])*1000*1000
            if 'KB/s' in rate:
                return float(rate[:-4])*1000
            if 'B/s' in rate:
                return float(rate[:-3])

        def translate_number_to_rate(number):
            '''把6530000.0转换成6.53Mb/s'''
            if number>1000:
                if number>1000*1000:
                    if number>1000*1000*1000:
                        unit='GB/s'
                        number=number/(1000*1000*1000)
                        return str(number)+unit
                    else:
                        unit='MB/s'
                        number=number/(1000*1000)
                        return str(number)+unit
                else:
                    unit='KB/s'
                    number=number/(1000)
                    return str(number)+unit
            else:
                unit='B/s'
                number=number
                return str(number)+unit
      
        df_create=pd.read_excel(self.create_performance_save_path,sheet_name='result')
        df_analyze=pd.read_excel(self.analyze_performance_save_path,sheet_name='result')
        create_cpu=float(df_create['cpu'][0][:-1])
        create_mem=float(df_create['mem'][0][:-1])
        create_free=float(df_create['free'][0][:-1])
        create_read=translate_rate_to_float(df_create['read'][0])
        create_write=translate_rate_to_float(df_create['write'][0])
        create_time=df_create['time'][0]
        analyze_cpu=float(df_analyze['cpu'][0][:-1])
        analyze_mem=float(df_analyze['mem'][0][:-1])
        analyze_free=float(df_analyze['free'][0][:-1])
        analyze_read=translate_rate_to_float(df_analyze['read'][0])
        analyze_write=translate_rate_to_float(df_analyze['write'][0])
        analyze_time=df_analyze['time'][0]
        create_scale=create_time/(create_time+analyze_time)
        analyze_scale=analyze_time/(create_time+analyze_time)
        cpu=create_cpu*create_scale+analyze_cpu*analyze_scale
        mem=create_mem*create_scale+analyze_mem*analyze_scale
        free=create_free*create_scale+analyze_free*analyze_scale
        read=create_read*create_scale+analyze_read*analyze_scale
        write=create_write*create_scale+analyze_write*analyze_scale
        time=create_time+analyze_time
        cpu=str(cpu)+'%'
        mem=str(mem)+'%'
        free=str(free)+'%'
        read=translate_number_to_rate(read)
        write=translate_number_to_rate(write)
        res={'cpu':[cpu],'mem':[mem],'free':[free],'read':[read],'write':[write],'time':[time]}
        df=pd.DataFrame(res)
        df.to_excel(self.performance_save_path,sheet_name='result')
        pass

    def get_name_cweid_dict(self):
        '''根据 codeql_cwe_name_excel_path 获取 name 字段与 cweid 对应关系的字典'''
        df=pd.read_excel(self.codeql_cwe_name_excel_path,sheet_name=self.codeql_cwe_name_excel_sheetname)
        for i in range(len(df['cweid'])):
            cweid=df['cweid'][i]
            cwe_name=df['cwe_name'][i]
            self.name_cweid_dict[cwe_name]=cweid

    def get_result_from_csv_file(self):
        '''根据 codeql 的最终 csv 解析结果生成 result 对象'''
        df=pd.read_csv(self.codeql_result_csv_path,names=['name','description','severity','message','path','start_line','start_column','end_line','end_column'])
        for i in range(len(df['name'])):
            name=df['name'][i]
            path=df['path'][i]
            line=str(df['start_line'][i])
            cweid=self.name_cweid_dict[name]
            self.result.append({'path':path,'line':line,'cweid':cweid})
    
    def analyze_result(self):
        '''对 result 对象进行解析,生成最终的结果'''
        # juliet 测试用例的总数
        juliet_testcases_number=0
        for item in self.juliet.result.values():
            juliet_testcases_number+=len(item)
        # 测试成功的结果数量
        success_report_number=0
        # codeql 重复报告相同的 juliet 测试用例数量
        codeql_repeat_number=0
        # codeql 的结果总数
        codeql_result_number=len(self.result)
        # 匹配成功的 juliet 的测试用例列表，避免同一个测试用例被多次算作 success_report_number 里
        success_juliet_testcase_list=[]
        testnumber=0
        # 循环遍历 result
        for item in self.result:
            # 根据文件名去 juliet 找对应的测试用例
            file_name=item['path'].split('/')[-1]
            juliet_testcase=self.juliet.get_testcase_by_file_name(file_name)
            # 如果找不到对应的测试用例就 continue
            if not juliet_testcase:
                continue
            # 获取 juliet 的测试用例对应的 cweid
            juliet_testcase_cweid=self.juliet.get_testcase_cweid(juliet_testcase)
            # 获取 codeql 的 cweid 与 juliet 的 cweid 之间的继承关系
            relations=self.cwe_root.get_relations(item['cweid'],juliet_testcase_cweid)
            # codeql 为 juliet 的祖辈或者相等或子辈
            if relations in ['ancestor','equal','descendant']:
                # 行数与 juliet 测试用例的行数匹配
                if self.juliet.if_mate_line(item['line'],file_name,juliet_testcase):
                    # 如果 juliet 的该测试用例没有被统计过,算一次成功的报告,成功报告数量加一，并且该测试用例记录下来
                    if juliet_testcase not in success_juliet_testcase_list:
                        success_report_number+=1
                        success_juliet_testcase_list.append(juliet_testcase)
                    # 如果该测试用例被统计过,记录到 codeql_repeat_number
                    else:
                        codeql_repeat_number+=1
        
        # 召回率/准确率：正确报告数与测试用例中用例总数的比值
        rcr=success_report_number/juliet_testcases_number
        # 误报率：错误报告数占工具所有报告总数的比值，错误报告数=codeql总报告数-codeql成功报告数-codeql重复报告数
        fpr=(codeql_result_number-success_report_number-codeql_repeat_number)/codeql_result_number
        excel_res={
            'juliet_testcases_number':[juliet_testcases_number],
            'codeql_result_number':[codeql_result_number],
            'success_report_number':[success_report_number],
            'rcr':[rcr],
            'fpr':[fpr],
        }
        df=pd.DataFrame(excel_res)
        df.to_excel('./analyze_result/codeql_analyze_result.xlsx', sheet_name='main')